#include "mcmcAffinityMap.h"
#include "mcmcLayerRegionElements.h"

float MCMC_Affinity::DistToQ(double dist)
{
	float q1;

	q1 = (float)(1.0-dist);

	q1 = (float)min(q1, 0.95);
	q1 = (float)max(q1, 0.1);

	return q1;
}

///////////////////////////////////////////////
///////////////////////////////////////////////

MCMC_Affinity* MCMC_Affinity_Intensity::CreateAndCopy(void) const
{
	MCMC_Affinity *pcue_new;

	pcue_new = new MCMC_Affinity_Intensity();
	*pcue_new = *this;

	return pcue_new;
}

// operations
void MCMC_Affinity_Intensity::ComputeBond4Region(MCMC_LowLevelStat4Region &a, MCMC_LowLevelStat4Region &b)
{
	double d;

	d = fabs(a.mean-b.mean)/250.0+sqrt(fabs(a.var-b.var))/350.0;
	
	q = DistToQ(d);

	if (a.num_pixel<10 || b.num_pixel<10)
		q = (float)max(q, 0.55);
	else if (a.num_pixel<50 || b.num_pixel<50)
		q = (float)max(q, 0.35);
}


///////////////////////////////////////////////
///////////////////////////////////////////////

MCMC_Affinity* MCMC_Affinity_Smooth::CreateAndCopy(void) const
{
	MCMC_Affinity *pcue_new;

	pcue_new = new MCMC_Affinity_Smooth();
	*pcue_new = *this;

	return pcue_new;
}


// operations
void MCMC_Affinity_Smooth::ComputeBond4Region(MCMC_LowLevelStat4Region &a, MCMC_LowLevelStat4Region &b)
{
	double d1,d2,d;

	// intensity difference
	d1 = a.his_intensity.DistKL(b.his_intensity)*0.3;
	// plane difference
	d2 = fabs(a.a-b.a)/75.0+fabs(a.b-b.b)/75.0+fabs(a.c-b.c)/250.0;
	
	d = min(d1, d2);
	
	q = DistToQ(d);

	q = (float)max(q, 0.05);

	if (a.num_pixel<10 || b.num_pixel<10)
		q = (float)max(q, 0.55);
	else if (a.num_pixel<50 || b.num_pixel<50)
		q = (float)max(q, 0.35);
}




///////////////////////////////////////////////
///////////////////////////////////////////////
MCMC_Affinity* MCMC_Affinity_Texture::CreateAndCopy(void) const
{
	MCMC_Affinity *pcue_new;

	pcue_new = new MCMC_Affinity_Texture();
	*pcue_new = *this;

	return pcue_new;
}


// operations
void MCMC_Affinity_Texture::ComputeBond4Region(MCMC_LowLevelStat4Region &a, MCMC_LowLevelStat4Region &b)
{
	double d;

	d = a.his_intensity.DistKL(b.his_intensity)*0.18;

	q = DistToQ(d);

	if (a.num_pixel<10 || b.num_pixel<10)
		q = (float)max(q, 0.55);
	else if (a.num_pixel<50 || b.num_pixel<50)
		q = (float)max(q, 0.35);
}


///////////////////////////////////////////////
///////////////////////////////////////////////
MCMC_Affinity*  CreateCue(const TypeOfCue type)
{
	MCMC_Affinity	*pcue_new;

	switch (type)
	{
	case TOC_Intensity:	pcue_new = new MCMC_Affinity_Intensity();
						return pcue_new;

	case TOC_Smooth:	pcue_new = new MCMC_Affinity_Smooth();
						return pcue_new;

	case TOC_Texture:	pcue_new = new MCMC_Affinity_Texture();
						return pcue_new;

	case TOC_Face:		return NULL;
	}
	return NULL;
}



///////////////////////////////////////////////
///////////////////////////////////////////////

// construnction & destruction
MCMC_CombinedAffinity::MCMC_CombinedAffinity()
{
	max_space = 5;

	p_cues = new PMCMC_Affinity[max_space];
	p_cues[0] = new MCMC_Affinity_Intensity();
	p_cues[1] = new MCMC_Affinity_Smooth();
	p_cues[2] = new MCMC_Affinity_Texture();

	num = 3;

	for (int i=3; i<max_space; i++)
		p_cues[i] = NULL;

	q = 0.0f;
	e = 0;
}

MCMC_CombinedAffinity::~MCMC_CombinedAffinity()
{
	Free();
}

void MCMC_CombinedAffinity::Free(void)
{
	for (int i=0; i<max_space; i++)
	{
		if (p_cues[i] != NULL)
			delete p_cues[i];
		p_cues[i] = NULL;
	}
	p_cues = NULL;
}


MCMC_CombinedAffinity & MCMC_CombinedAffinity::operator=(const MCMC_CombinedAffinity &a)
{
	if (this != &a)
	{
		Free();

		// allocate space
		max_space = a.MaxSpace();
		num = a.Num();
		p_cues = new PMCMC_Affinity[max_space];

		for (int i=0; i<max_space; i++)
		{
			if (a.p_cues[i] != NULL)
				p_cues[i] = a.p_cues[i]->CreateAndCopy();
			else
				p_cues[i] = NULL;
		}

		q = a.q;
		e = a.e;
	}

	return *this;
}

// operations
void MCMC_CombinedAffinity::ExpandSpaceTo(const int max_space_new)
{
	if (max_space >= max_space_new || max_space_new<=0)
		return;

	PMCMC_Affinity	*p_cues_new;

	p_cues_new = new PMCMC_Affinity[max_space_new];
	if (p_cues != NULL)
	{
		for (int i=0; i<max_space; i++)
			p_cues_new[i] = p_cues[i];
		delete []p_cues;
	}
	p_cues = p_cues_new;
	max_space = max_space_new;
}


void MCMC_CombinedAffinity::SampleEdge(RF &rf_on_off)
{
	if (q <= 0.0f)
		e = 0;
	else if (q >= 1.0f)
		e = 1;
	else
	{
		rf_on_off.SetOneBin(0, 1-q);
		rf_on_off.SetOneBin(1, q);
		rf_on_off.normalize();

		e = IntClose(rf_on_off.SamplingNonSetCdf());
	}
}

void MCMC_CombinedAffinity::ComputeAffinitiesOnAllCues(MCMC_LowLevelStat4Region &a, MCMC_LowLevelStat4Region &b)
{
	if (p_cues == NULL)
		return;

	for (int i=0; i<Num(); i++)
	{
		if (p_cues[i] != NULL)
			p_cues[i]->ComputeBond4Region(a, b);
	}
}


void MCMC_CombinedAffinity::ComputeCombinedQ(mcmcVector<int> &vc_cue_type, MethodOfCombiningCues flag_method)
{
	int			i;
	MCMC_Affinity	*pcue;

	switch (flag_method)
	{
	case MOCC_biggest:	q = 0.0f;
						for (i=0; i<vc_cue_type.size(); i++)
						{
							pcue = GetCue(vc_cue_type(i));
							assert(pcue != NULL);
							if (q < pcue->q)
								q = pcue->q;
						}

						break;

	case MOCC_product:	q = 1.0f;
						for (i=0; i<vc_cue_type.size(); i++)
						{
							pcue = GetCue(vc_cue_type(i));
							assert(pcue != NULL);
							q *= pcue->q;
						}
						break;
	}
	q = (float)min(0.95, q);
	q = (float)max(0.05, q);
}


////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
MCMC_ArrayManagement::MCMC_ArrayManagement()
{
	head_active = -1;
	tail_active = -1;
	num_of_active = 0;
	head_recyle = 0;
	current = -1;
}

MCMC_ArrayManagement::~MCMC_ArrayManagement()
{
}

void MCMC_ArrayManagement::Empty(void)
{
	head_active = -1;
	tail_active = -1;
	num_of_active = 0;
	head_recyle = 0;
	current = -1;

	for (int i=0; i<vc_next.size(); i++)
	{
		vc_next(i) = i+1;
		vc_last(i) = i-1;
		vc_sign(i) = 0;
	}
	vc_next(vc_next.size()-1) = -1;
}

MCMC_ArrayManagement & MCMC_ArrayManagement::operator=(const MCMC_ArrayManagement &a)
{
	if (this != &a)
	{
		head_active		= a.head_active;
		tail_active		= a.tail_active;
		num_of_active	= a.num_of_active;
		head_recyle		= a.head_recyle;
		current			= a.current;

		vc_next			= a.vc_next;
		vc_last			= a.vc_last;
		vc_sign			= a.vc_sign;
	}

	return *this;
}



// operations
void MCMC_ArrayManagement::Init(const int max_num)
{
	vc_next.SetRowVector(max_num);
	vc_last.SetRowVector(max_num);

	vc_sign.SetRowVector(max_num);

	Empty();
}

void MCMC_ArrayManagement::ExpandTo(const int max_new)
{
	if (max_new <= Max())
		return;

	int max_old;
	max_old = Max();

	vc_next.ExpandTo(max_new);
	vc_last.ExpandTo(max_new);
	vc_sign.ExpandTo(max_new);

	int i;

	for (i=max_old; i<max_new; i++)
	{
		vc_next(i) = i+1;
		vc_last(i) = i-1;
		vc_sign(i) = 0;
	}
	vc_last(max_old) = -1;
	vc_next(vc_next.size()-1) = -1;

	int move;

	move = head_recyle;
	if (move < 0)
		head_recyle = max_old;
	else
	{
		while (vc_next(move) >= 0)
			move = vc_next(move);
		vc_next(move) = max_old;
		vc_last(max_old) = move;
	}
}


int MCMC_ArrayManagement::New(void)
{
	int id_new;
	id_new = head_recyle;

	if (id_new >= Max() || id_new < 0)
		return -1;

	assert(vc_sign.Data(id_new) == 0);
	CreateRecord(id_new);

	return id_new;
}

int	MCMC_ArrayManagement::New(const int id)
{
	if (!Valid(id) || vc_sign(id)!=0)
		return -1;

	CreateRecord(id);

	return id;
}


void MCMC_ArrayManagement::CreateRecord(int id_new)
{
	// delete it from the recycle list
	int id_next,id_last;

	id_next = vc_next(id_new);
	id_last = vc_last(id_new);
	if (Valid(id_last))
		vc_next(id_last) = id_next;
	if (Valid(id_next))
		vc_last(id_next) = id_last;
	if (head_recyle == id_new)
		head_recyle = vc_next(id_new);
	vc_next(id_new) = -1;
	vc_last(id_new) = -1;

	// put this region to the active list
	if (head_active == -1)
	{
		head_active = id_new;
		tail_active = head_active;
	}
	else
	{
		vc_next(tail_active) = id_new;
		vc_last(id_new) = tail_active;
		tail_active = id_new;
	}
	vc_sign(id_new) = 1;
	num_of_active++;
}


bool MCMC_ArrayManagement::Delete(const int id)
{
	if (!Valid(id) || vc_sign(id)==0)
		return false;

	if (current == id)
		current = vc_next(id);

	// delete it from the active list
	int id_next,id_last;

	id_next = vc_next(id);
	id_last = vc_last(id);
	if (Valid(id_last))
		vc_next(id_last) = id_next;
	if (Valid(id_next))
		vc_last(id_next) = id_last;
	if (head_active == id)
		head_active = vc_next(id);
	if (head_active < 0)
		tail_active = head_active;

	vc_last(id) = -1;

	// put it to the recyle list
	vc_next(id) = head_recyle;
	if (Valid(head_recyle))
		vc_last(head_recyle) = id;
	head_recyle = id;

	// update
	vc_sign(id) = 0;
	num_of_active--;

	return true;
}

////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////

// construnction & destruction
MCMC_AffinityMap::MCMC_AffinityMap()
{
	max_space = 10;

	p_combined_cues_hashed =  new PMCMC_CombinedAffinity[max_space];
	for (int i=0; i<max_space; i++)
		p_combined_cues_hashed[i] = NULL;

	vc_id_hashed.SetRowVector(max_space);
	vc_id_hashed.InitValue(-1);

	p_combined_cues_extn	= new PMCMC_CombinedAffinity[max_space];
	for (int i=0; i<max_space; i++)
		p_combined_cues_extn[i] = NULL;

	vc_id_extn.SetRowVector(max_space);
	vc_id_extn.InitValue(-1);

	// space management
	mag_hash.Init(max_space);
	mag_extn.Init(max_space);
}

MCMC_AffinityMap::~MCMC_AffinityMap()
{
	Empty();

	Free();
}

void MCMC_AffinityMap::Empty(void)
{
	int i;

	for (i=0; i<max_space; i++)
	{
		if (p_combined_cues_hashed[i] != NULL)
			delete p_combined_cues_hashed[i];
		p_combined_cues_hashed[i] = NULL;

		if (p_combined_cues_extn[i] != NULL)
			delete p_combined_cues_extn[i];
		p_combined_cues_extn[i] = NULL;
	}

	vc_id_hashed.InitValue(-1);
	vc_id_extn.InitValue(-1);

	// initialize the lists
	mag_hash.Empty();
	mag_extn.Empty();
}

void MCMC_AffinityMap::Free(void)
{
	if (p_combined_cues_hashed != NULL)
		delete []p_combined_cues_hashed;
	p_combined_cues_hashed = NULL;

	if (p_combined_cues_extn != NULL)
		delete []p_combined_cues_extn;
	p_combined_cues_extn = NULL;
}


// operators
MCMC_AffinityMap & MCMC_AffinityMap::operator=(const MCMC_AffinityMap &a)
{
	if (this != &a)
	{
		Empty();

		if (MaxSpace() != a.MaxSpace() && a.MaxSpace()>0)
		{
			// reallocate space
			Free();

			max_space = a.max_space;

			p_combined_cues_hashed = new PMCMC_CombinedAffinity[max_space];
			p_combined_cues_extn = new PMCMC_CombinedAffinity[max_space];
		}
		int i;

		// hash table
		for (i=0; i<MaxSpace(); i++)
		{
			if (a.p_combined_cues_hashed[i] != NULL)
			{
				p_combined_cues_hashed[i] = new MCMC_CombinedAffinity();
				*p_combined_cues_hashed[i] = *a.p_combined_cues_hashed[i];
			}
			else
				p_combined_cues_hashed[i] = NULL;

			if (a.p_combined_cues_extn[i] != NULL)
			{
				p_combined_cues_extn[i] = new MCMC_CombinedAffinity();
				*p_combined_cues_extn[i] = *a.p_combined_cues_extn[i];
			}
			else
				p_combined_cues_extn[i] = NULL;
		}
		vc_id_hashed	= a.vc_id_hashed;
		vc_id_extn		= a.vc_id_extn;

		mag_hash		= a.mag_hash;
		mag_extn		= a.mag_extn;
	}

	return *this;
}

// operations
void MCMC_AffinityMap::ExpandTo(const int max_space_new)
{
	if (max_space_new < max_space)
		return;

	PMCMC_CombinedAffinity	*p_temp;
	int				i;

	// allocate new space
	p_temp = new PMCMC_CombinedAffinity[max_space_new];
	vc_id_hashed.ExpandTo(max_space_new);

	for (i=0; i<max_space; i++)
		p_temp[i] = p_combined_cues_hashed[i];

	for (i=max_space; i<max_space_new; i++)
	{
		p_temp[i] = NULL;
		vc_id_hashed(i) = -1;
	}

	delete []p_combined_cues_hashed;
	p_combined_cues_hashed = p_temp;

	// allocate new space
	p_temp = new PMCMC_CombinedAffinity[max_space_new];
	vc_id_extn.ExpandTo(max_space_new);

	for (i=0; i<max_space; i++)
		p_temp[i] = p_combined_cues_extn[i];

	for (i=max_space; i<max_space_new; i++)
	{
		p_temp[i] = NULL;
		vc_id_extn(i) = -1;
	}

	delete []p_combined_cues_extn;
	p_combined_cues_extn = p_temp;

	// extend the list
	mag_hash.ExpandTo(max_space_new);
	mag_extn.ExpandTo(max_space_new);

	max_space = max_space_new;

	// can those in the extion table be moved to the hashed table
	int		index;

	i = mag_extn.GetFirst();
	while (i >= 0)
	{
		index = HashIndex(vc_id_extn(i));
		if (vc_id_hashed(index) < 0)
		{
			p_combined_cues_hashed[index] = p_combined_cues_extn[i];
			p_combined_cues_extn[i] = NULL;

			vc_id_hashed(index) = vc_id_extn(i);
			vc_id_extn(i) = -1;

			mag_extn.Delete(i);
			mag_hash.New(index);

			i = mag_extn.GetCurrent();
		}
		else
			i = mag_extn.GetNext();
	}
}


MCMC_CombinedAffinity* MCMC_AffinityMap::GetAffinity(const int id)
{
	if (id < 0)
		return NULL;

	int index=HashIndex(id);
	if (vc_id_hashed(index) == id)
		return p_combined_cues_hashed[index];		// in the hash table
	else
	{
		index = mag_extn.GetFirst();
		while (index >= 0)
		{
			if (vc_id_extn(index) == id)
				return p_combined_cues_extn[index];

			index = mag_extn.GetNext();
		}
	}
	return NULL;
}

MCMC_CombinedAffinity* MCMC_AffinityMap::NewAffinity(const int id)
{
	MCMC_CombinedAffinity	*ptemp;

	ptemp = GetAffinity(id);
	if (ptemp != NULL)
		return ptemp;

	if (Num() == MaxSpace())
		ExpandTo(MaxSpace()*2);

	int index=HashIndex(id);
	if (vc_id_hashed(index) < 0)
	{
		assert(p_combined_cues_hashed[index] == NULL);
		if (p_combined_cues_hashed[index] == NULL)
			p_combined_cues_hashed[index] = new MCMC_CombinedAffinity();
		vc_id_hashed(index) = id;

		mag_hash.New(index);

		return p_combined_cues_hashed[index];
	}
	else
	{
		index = mag_extn.GetFirstInRecyle();
		assert(index >= 0);
		mag_extn.New(index);

		assert(p_combined_cues_extn[index] == NULL);
		if (p_combined_cues_extn[index] == NULL)
			p_combined_cues_extn[index] = new MCMC_CombinedAffinity();
		vc_id_extn(index) = id;

		return p_combined_cues_extn[index];
	}
	return NULL;
}


bool MCMC_AffinityMap::DeleteAffinity(const int id)
{
	int index=HashIndex(id);
	if (vc_id_hashed(index) == id)
	{
		assert(p_combined_cues_hashed[index] != NULL);
		delete 	p_combined_cues_hashed[index];
		p_combined_cues_hashed[index] = NULL;

		vc_id_hashed(index) = -1;

		mag_hash.Delete(index);

		return true;
	}
	else
	{
		index = mag_extn.GetFirst();
		while (index >= 0)
		{
			if (vc_id_extn(index) == id)
			{
				mag_extn.Delete(index);

				assert(p_combined_cues_extn[index] != NULL);
				delete 	p_combined_cues_extn[index];
				p_combined_cues_extn[index] = NULL;
				vc_id_extn(index) = -1;

				return true;
			}
			index = mag_extn.GetNext();
		}
	}
	return false;
}

void MCMC_AffinityMap::PrepareBondsWith(mcmcVector<int> &vc_cue_type, mcmcVector<int> &vc_indexid)
{
	int	i;
	MCMC_CombinedAffinity*	pcombined_cues;

	for (i=0; i<vc_indexid.size(); i++)
	{
		pcombined_cues = GetAffinity(vc_indexid.Data(i));
		if (pcombined_cues != NULL)
			pcombined_cues->ComputeCombinedQ(vc_cue_type, MCMC_CombinedAffinity::MOCC_biggest);
	}
}


void MCMC_AffinityMap::PrepareBondsMasked(mcmcVector<int> &vc_cue_type, mcmcVector<int> *pvc_mask_indexid)
{
	MCMC_CombinedAffinity*	pcues;

	if (pvc_mask_indexid == NULL)
	{
		// try all
		pcues = GetFirstCombinedCue();
		while (pcues != NULL)
		{
			pcues->ComputeCombinedQ(vc_cue_type, MCMC_CombinedAffinity::MOCC_biggest);

			pcues = GetNextCombinedCue();
		}
	}
	else
	{
		int indexid;

		// check
		pcues = GetFirstCombinedCue(&indexid);
		while (pcues != NULL)
		{
			if (pvc_mask_indexid->Data(indexid) >= 0)
				pcues->ComputeCombinedQ(vc_cue_type, MCMC_CombinedAffinity::MOCC_biggest);

			pcues = GetNextCombinedCue(&indexid);
		}
	}
}


void MCMC_AffinityMap::RemoveEdges(void)
{
	MCMC_CombinedAffinity*	pcues;

	pcues = GetFirstCombinedCue();
	while (pcues != NULL)
	{
		// remove the edge
		pcues->TurnOffEdge();

		pcues = GetNextCombinedCue();
	}
}


void MCMC_AffinityMap::SampleEdges(mcmcMatrix<char> &mx_sampled_edges, RF &rf_on_off,
								  const int indexid1, mcmcVector<int> *pvc_mask_indexid)
{
	MCMC_CombinedAffinity*	pcues;
	int indexid2;

	if (pvc_mask_indexid == NULL)
	{
		// try all
		pcues = GetFirstCombinedCue(&indexid2);
		while (pcues != NULL)
		{
			// sample the edge
			pcues->SampleEdge(rf_on_off);
			// put it into the matrx
			mx_sampled_edges(indexid1, indexid2) = pcues->Edge();
			mx_sampled_edges(indexid2, indexid1) = pcues->Edge();

			pcues = GetNextCombinedCue(&indexid2);
		}
	}
	else
	{
		// check
		pcues = GetFirstCombinedCue(&indexid2);
		while (pcues != NULL)
		{
			if (pvc_mask_indexid->Data(indexid2) >= 0)
			{
				// sample the edge
				pcues->SampleEdge(rf_on_off);
				// put it into the matrx
				mx_sampled_edges(indexid1, indexid2) = pcues->Edge();
				mx_sampled_edges(indexid2, indexid1) = pcues->Edge();
			}

			pcues = GetNextCombinedCue(&indexid2);
		}
	}
}
